﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
using NLog;

namespace Y{

    public class Server {
        static private Logger logger = LogManager.GetCurrentClassLogger();

        public event EventHandler<ReceivedEventArgs> Received;
        private TcpListener tcpListener = new TcpListener(IPAddress.Any, 502);
        private List<TcpClient> _tcpClients = new List<TcpClient>();
        private MemoryStream _ms = new MemoryStream(20 * 1024 * 1024);
        private const int size = 5 * 1024 * 1024;
        //const int size = 2;
        private byte[] buffer = new byte[size];

        /// <summary>
        /// 
        /// </summary>
        public Server() {

        }

        /// <summary>
        /// 
        /// </summary>
        public void Start() {
            logger.Debug("Server.Start");
            tcpListener.Start();
            tcpListener.BeginAcceptTcpClient(AcceptTcpClientCallback, null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ar"></param>
        private void AcceptTcpClientCallback(IAsyncResult ar) {
            logger.Debug("AcceptTcpClientCallback");

            var tcpClient = tcpListener.EndAcceptTcpClient(ar);
            tcpListener.BeginAcceptTcpClient(AcceptTcpClientCallback, null);

            var networkStream = tcpClient.GetStream();
            if (SafeBeginRead(networkStream)) {

                logger.Debug("_tcpClients.Count: {0}", _tcpClients.Count);

                if (_tcpClients.Count > 100) {
                    var first = _tcpClients.First();
                    _tcpClients.Remove(first);
                    CloseTcpClient(first);
                }

                this._tcpClients.Add(tcpClient);
            }
            else{
                CloseTcpClient(tcpClient);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tcpClient"></param>
        private void CloseTcpClient(TcpClient tcpClient) {
                //networkStream.Close();
                tcpClient.Close();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ns"></param>
        /// <returns></returns>
        private bool SafeBeginRead(NetworkStream ns) {
            logger.Debug("SafeBeginRead");

            try {
                ns.BeginRead(buffer, 0, size, StreamReadCallback, ns);
                return true;
            }
            catch (IOException ioEx) {
                ProcessException(ioEx);
            }
            catch (ObjectDisposedException odEx) {
                ProcessException(odEx);
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="networkStream"></param>
        /// <param name="ar"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        private bool SafeEndRead(NetworkStream networkStream,  IAsyncResult ar, out int n) {
            n = -1;
            try {
                n = networkStream.EndRead(ar);
                logger.Debug("networkStream.EndRead(ar): {0}", n);
                return true;
            }
            catch (IOException ioEx) {
                ProcessException(ioEx);
            }
            catch (ObjectDisposedException odEx) {
                ProcessException(odEx);
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ar"></param>
        private void StreamReadCallback(IAsyncResult ar) {
            int n;
            var networkStream = (NetworkStream)ar.AsyncState;
            if (SafeEndRead(networkStream, ar, out n)) {
                logger.Debug("SafeEndRead: true, n: {0}", n);

                if (n > 0) {
                    _ms.Write(buffer, 0, n);

                    logger.Debug("networkStream.DataAvailable: {0}", networkStream.DataAvailable);

                    if (!networkStream.DataAvailable) {
                        OnReceiveCompleted(networkStream);
                    }

                    // read completed
                    //
                    SafeBeginRead(networkStream);
                }
                else if (n == 0) {
                    logger.Debug("n==0, _ms.Length: {0}", _ms.Length);

                    var bs = _ms.ToArray();
                    _ms.SetLength(0);
                    //logger.Debug("complete: " + bs.Length);
                    networkStream.Close();
                    logger.Debug("ns.close()");
                }
            }
            else {
                logger.Debug("SafeEndRead: false");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void OnReceiveCompleted(NetworkStream networkStream) {
            var bs = _ms.ToArray();
            _ms.SetLength(0);
            //logger.Debug("complete: " + bs.Length);

            if (Received != null) {
                Received(this, new ReceivedEventArgs(networkStream, bs));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ex"></param>
        private void ProcessException(Exception ex) {
            logger.Debug(ex); 
        }
    }
}
